/*
 * Alex Veltsistas G00651470
 * Josh Hornsby G00721274
 * 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package proj3;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.Socket;
import java.text.DecimalFormat;
import java.util.ArrayList;

/**
 * A client thread for the auction server
 * @author alex
 */
public class ClientThread extends Thread
{
    private static final Object globalLock = new Object(); //locks all clients accessing item database
    public final Object threadLock = new Object(); //locks an individual client thread
    private final ArrayList<String> userMessages = new ArrayList<>();
    private Socket client;
    private InputStream input;
    private OutputStream output;
    private String user = "";
    private boolean isSeller;
    
    public void addUserMessage(String message)
    {//locks a thread and adds a message to its outbound queue
        synchronized(threadLock)
        {
            userMessages.add(message);
        }
    }
    public void setClient(Socket client)
    {
        this.client = client;
    }
    public String getUserName()
    {
        return user;
    }
    private String getCommand() throws IOException
    {
        String s = "";
        while(!s.endsWith("<**>") && AuctionServer.flag)
        {
            if(userMessages.size() > 0)
            {//check for messages
                synchronized(threadLock)
                {
                    for(int x=0;x<userMessages.size();x++)
                        output.write(userMessages.get(x).getBytes("UTF-8"));
                    userMessages.clear();
                }
            }
            while(input.available() > 0 && !s.endsWith("<**>"))
            {
                s += (char)input.read();
            }
            Thread.yield();
        }
        return s.substring(0, s.length() - 4);
    }
    private static synchronized String attemptLogin(String userName, String userType, ClientThread t) throws IOException
    {//only one login is attempted at a time because we want to make sure there is only 1 of each user name
        if(AuctionServer.isUserNameValid(userName))
        {
            boolean seller = userType.charAt(0) == 's';
            if(!seller && userName.toLowerCase().equals("seller"))
                return "That username is reserved for the seller.\n";
            t.user = userName;
            t.isSeller = seller; 
            if(t.isSeller)
                return "Welcome "+ userName + " you are now logged in as a SELLER.\n";
            else
                return "Welcome "+ userName + " you are now logged in as a BUYER.\n";
        }
        else
            return "A user with that name is already in the system.\n";
    }
    @Override
    public void run() 
    {
        try
        {
            input = client.getInputStream();
            output = client.getOutputStream();
            while(AuctionServer.flag)
            {
                String[] s = getCommand().split(" ");
                if(user.equals("") && !s[0].equals("login"))
                    output.write("A user must login before issuing commands.\n".getBytes("UTF-8"));
                else
                {
                    switch(s[0])
                    {
                        case "login":
                            if(s.length != 3)
                                output.write("The login command was invalid. Example: login alex\n".getBytes("UTF-8"));
                            else
                            {
                                s[1] = s[1].trim();
                                if (s[1].length() < 2)
                                    output.write("A user name must be at least 4 non whitespace characters.\n".getBytes("UTF-8"));
                                else
                                    output.write(attemptLogin(s[1],s[2],this).getBytes("UTF-8"));
                            }
                            break;
                        case "list":
                            synchronized(globalLock)
                            {
                                String listStr = "Item Number\tItem Name\tCurrent Bid\tBidder\n";
                                listStr += "--------------------------------------------------------\n";
                                DecimalFormat df = new DecimalFormat("0.00");
                                for(int x = 0; x < AuctionServer.items.size(); x++)
                                {
                                    AuctionItem i = AuctionServer.items.get(x);
                                    listStr +=  i.getItemNumber() + "\t" + i.getItemName() + "\t$" + df.format(i.getHighestBid()) + "\t" + i.getHighestBidder() + "\n";
                                }
                                output.write(listStr.getBytes("UTF-8"));
                            }
                            break;
                        case "bid":
                            if(s.length != 3)
                                output.write("The bid command was invalid. Example: bid 52467 55.47\n".getBytes("UTF-8"));
                            else
                            {
                                int itemNum;
                                float amount;
                                try
                                {
                                    itemNum = Integer.parseInt(s[1]);
                                    amount = Float.parseFloat(s[2]);
                                    amount = (float)(Math.floor(amount*100)/100);
                                }
                                catch (NumberFormatException ex)
                                {
                                    output.write(("The id or bid provided was invalid.\n").getBytes("UTF-8"));
                                    break;
                                }
                                synchronized(globalLock)
                                {
                                    AuctionItem i = AuctionServer.findItem(itemNum);
                                    if(i != null)
                                    {
                                        DecimalFormat df = new DecimalFormat("0.00"); 
                                        if(amount <= i.getHighestBid())
                                            output.write(("You must bid more than $" + df.format(i.getHighestBid()) + ".\n").getBytes("UTF-8"));
                                        else
                                        {
                                            //inform old bidder
                                            if(!i.getHighestBidder().equals(""))
                                            {
                                                ClientThread t = AuctionServer.findUser(i.getHighestBidder());
                                                if(t != null)
                                                    t.addUserMessage("You were out bid! Item: " + i.getItemNumber() + ", Name: " + i.getItemName() + ", Price: $" + df.format(amount) + ".\n");
                                            }
                                            i.setHighestBidder(user);
                                            i.setHighestBid(amount);
                                            output.write(("You are now the highest bidder! Item: " + i.getItemNumber() + ", Name: " + i.getItemName() + ", Price: $" + df.format(amount) + ".\n").getBytes("UTF-8"));
                                        }
                                    }        
                                    else
                                        output.write(("An item with id: " + itemNum + " could not be found.\n").getBytes("UTF-8"));
                                }
                            }
                            break;
                        case "add":
                            if(s.length < 3)
                                output.write("The add command was invalid. Example: add 52467 Scuba Gear\n".getBytes("UTF-8"));
                            else
                            {
                                for(int x = 3; x < s.length;x++)
                                    s[2] += " " + s[x];
                                int itemNum;
                                try
                                {
                                    itemNum = Integer.parseInt(s[1]);
                                }
                                catch (NumberFormatException ex)
                                {
                                    output.write(("The id provided: " + s[1] + " is invalid\n").getBytes("UTF-8"));
                                    break;
                                }
                                synchronized(globalLock)
                                {
                                    if(AuctionServer.containsItem(itemNum))
                                        output.write("An item with the same id already exists.\n".getBytes("UTF-8"));
                                    else
                                    {
                                        AuctionItem i = new AuctionItem(itemNum, s[2]);
                                        AuctionServer.items.add(i);
                                        output.write("Your item has been added.\n".getBytes("UTF-8"));
                                    }
                                }
                            }
                            break;
                        case "sell":
                            if(s.length != 2)
                                output.write("The sell command was invalid. Example: sell 52467\n".getBytes("UTF-8"));
                            else
                            {
                                int itemNum;
                                try
                                {
                                    itemNum = Integer.parseInt(s[1]);
                                }
                                catch (NumberFormatException ex)
                                {
                                    output.write(("The id provided: " + s[1] + " is invalid\n").getBytes("UTF-8"));
                                    break;
                                }
                                synchronized(globalLock)
                                {
                                    AuctionItem i = AuctionServer.findItem(itemNum);
                                    if(i != null)
                                    {
                                        if(!i.getHighestBidder().equals(""))
                                        {
                                            ClientThread t = AuctionServer.findUser(i.getHighestBidder());
                                            if(t != null)
                                            {
                                                t.addUserMessage("You have won the auction on Item: " + i.getItemNumber() + ", Name: " + i.getItemName() + ".\n");
                                                output.write(("You have sold Item: " + i.getItemNumber() + " to Buyer: " + i.getHighestBidder() + ".\n").getBytes("UTF-8"));
                                                AuctionServer.items.remove(i);
                                            }
                                            else
                                            {
                                                output.write(("Were terribly sorry but the buyer for Item: " + i.getItemNumber() + " has disconnected. Your item will be reset to $0.00 sorry for the inconvenience.\n").getBytes("UTF-8"));
                                                i.setHighestBid(0f);
                                                i.setHighestBidder("");
                                            }
                                        }
                                        else
                                            output.write("No one has bid on your item yet.\n".getBytes("UTF-8"));
                                    }
                                    else
                                        output.write("An item with the id provided does not exist.\n".getBytes("UTF-8"));
                                }
                            }
                            break;      
                        case "exit":
                            synchronized(globalLock)
                            {//remove user from system, free the socket and exit the thread
                                user = "";
                                this.client.close();
                            }
                            return;
                        default:
                            output.write(("The command \"" + s[0] + "\" is unsupported.\n").getBytes("UTF-8"));
                    }
                }
            }
        }
        catch(Exception ex)
        {//Issue User: bob Ip: 53545435
            System.out.println("Client exception handled for User: " + user + " IP: " + client.getInetAddress().toString() + "\n" + ex.getMessage());
        }
        // synchronized(mylock) {}
    }
}